<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>爪哇 - 符合PromiseA+规范的promise</title>
</head>

<body>
    <script>
        const PENDING = 'pending'
        const FULFILLED = 'fulfilled'
        const REJECTED = 'rejected'

        class MPromise {
            FULFILLED_CALLBACK_LIST = []
            REJECTED_CALLBACK_LIST = []

            _status = PENDING

            constructor(fn) {
                this.status = PENDING
                this.value = null
                this.reason = null

                try {
                    fn(this.resolve.bind(this), this.reject.bind(this))
                } catch (error) {
                    this.reject(error)
                }
            }

            get status() {
                return this._status
            }

            set status(newStatus) {
                this._status = newStatus
                switch (newStatus) {
                    case FULFILLED:
                        this.FULFILLED_CALLBACK_LIST.forEach(callback => {
                            callback(this.value)
                        })
                        break;
                    case REJECTED:
                        this.REJECTED_CALLBACK_LIST.forEach(callback => {
                            callback(this.reason)
                        })
                        break;
                }
            }

            resolve(value) {
                if (this.status === PENDING) {
                    this.value = value
                    this.status = FULFILLED
                }
            }

            reject(reason) {
                if (this.status === PENDING) {
                    this.reason = reason
                    this.status = REJECTED
                }
            }

            then(onFulfilled, onRejected) {
                const fulFilledFn = this.isFunction(onFulfilled) ? onFulfilled : (value) => value
                const rejectedFn = this.isFunction(onRejected) ? onRejected : (reason) => {
                    throw (reason)
                }

                const fulFilledFnWithCatch = (resolve, reject, newPromise) => {
                    try {
                        if (!this.isFunction(onFulfilled)) {
                            resolve(this.value)
                        } else {
                            const x = fulFilledFn(this.value)
                            this.resolvePromise(newPromise, x, resolve, reject)
                        }
                        fulFilledFn(this.value)
                    } catch (e) {
                        reject(e)
                    }
                }

                const rejectedFnWithCatch = (resolve, reject, newPromise) => {
                    try {
                        if (!this.isFunction(onRejected)) {
                            reject(this.reason)
                        } else {
                            const x = rejectedFn(this.reason)
                            this.resolvePromise(newPromise, x, resolve, reject)
                        }
                    } catch (e) {
                        reject(e)
                    }
                }

                switch (this.status) {
                    case FULFILLED:
                        const newPromise = new MPromise((resolve, reject) => {
                            fulFilledFnWithCatch(resolve, reject, newPromise)
                        })
                        return newPromise
                        break;
                    case REJECTED:
                        const newPromise = new MPromise((resolve, reject) => {
                            rejectedFnWithCatch(resolve, reject, newPromise)
                        })
                        return newPromise
                        break;
                    case PENDING:
                        const newPromise = new MPromise((resolve, reject) => {
                            this.FULFILLED_CALLBACK_LIST.push(() => fulFilledFnWithCatch(resolve, reject, newPromise))
                            this.REJECTED_CALLBACK_LIST.push(() => rejectedFnWithCatch(resolve, reject, newPromise))
                        })
                        return newPromise
                }
            }

            resolvePromise(newPromise, x, resolve, reject) {

            }

            isFunction(param) {
                return typeof param === 'function'
            }

        }
    </script>
</body>

</html>